
#encoding: latin-1

from Variables import *
from Plateau import *

class Joueur:
    """ Stocke les constantes nécessaires pour identifier un joueur Humain. """
    
    def __init__(self, othello, couleur):
        """ Constructeur. """
        
        self.othello = othello
        self.couleur = couleur
        if couleur == "blanc":
            self.joueur = 1
        elif couleur == "noir":
            self.joueur = 2
        
class Ordinateur():
    """ Stocke les constantes et méthodes nécessaires pour identifier et contrôler un joueur Ordinateur. Les coups joués seront calculés avec l'algorihtme alpha-beta, version optimisées de l'algorithme du min-max. """
    
    def __init__(self, othello, couleur):
        """ Constructeur. """
        
        self.othello = othello
        self.couleur = couleur
        if couleur == "blanc":
            self.joueur = 1
        elif couleur == "noir":
            self.joueur = 2
        self.niveau = 4 # Niveau de profondeur de recherche du coup idéal par l'ordinateur. Il est possible d'augmenter le niveau si le jeu tourne suffisamment vite.
        self.meilleurCoup = -1
    
    def reflexionJoueur(self):
        """ Débute la reflexion de l'ordinateur lorsque c'est à lui de jouer. Cette fonction va calculer le coup que va jouer l'ordinateur. Il calcule son coup en passant par l'algo alpha-beta à travers la fonction reflexion."""

        # On calcule le meilleur coup possible
        self.meilleurCoup = -1
        score = self.reflexion(self.othello.plateau, 0, self.joueur, -INFINITY, INFINITY, False)
        coup = self.meilleurCoup
        # On joue le coup
        self.othello.plateau.coup(coup, self.othello.joueurMain.joueur)
        # On met à jour le plateau graphique et le score affiché
        self.othello.qtPlateau.updateCases()
        self.othello.qtPlateau.updateScore()
                    
    def reflexion(self, plateau, profondeur, joueur, A, B, passe):
        """ Etude de tous les coups possibles pour l'ordinateur : c'est l'algorithme alpha-beta qui optimise l'algorithme du min-max"""

        if self.meilleurCoup >= 0 or profondeur != 0:
            return -INFINITY + 1

        joueurprinc = self.joueur
        
        if profondeur == self.niveau:
            s = plateau.scorePosition()
            return s[joueurprinc-1] - s[2-joueurprinc]

        coupsPossibles = plateau.coupsPossibles(joueur)
        nbCoupsPossibles = len(coupsPossibles)
           
        # Cas 1 : le joueur courant n'est pas le joueur principal (celui ayant initié l'algorithme)
        if joueurprinc != joueur:
            if nbCoupsPossibles == 0:        # Si on ne peut pas jouer
                if passe:                    # Si le joueur précédent avait également du passer son tour
                    s = plateau.scorePosition()
                    return s[joueurprinc-1] - s[2-joueurprinc]

                leplateau = Plateau(plateau) # On copie le plateau pour ne pas modifier l'original
                return self.reflexion(leplateau, profondeur, joueurprinc, A, B, True)
                
            beta = B
            for coup in coupsPossibles:      # On boucle sur tout les coups, qui représentent un niveau de profondeur de l'arbre
                leplateau = Plateau(plateau) # On copie le plateau pour ne pas modifier l'original
                leplateau.coup(coup, joueur) # On joue le coup
                resultat = self.reflexion(leplateau, profondeur+1, joueurprinc, A, beta, False)
                if resultat < beta:
                    beta = resultat
                    if A >= beta: return beta
            return beta
            
        # Cas 2 : le joueur courant est le joueur principal (celui ayant initié l'algorithme)
        else:
            adversaire = 3 - joueur          # On s'assure de choisir l'adversaire
            if nbCoupsPossibles == 0:        # Si on ne peut pas jouer
                if passe:                    # Si le précédent joueur n'avait pas pu jouer non plus
                    s = plateau.scorePosition()
                    return s[joueurprinc-1] - s[2-joueurprinc]        
            
                leplateau = Plateau(plateau) # On copie le plateau pour ne pas modifier l'original
                return self.reflexion(leplateau, profondeur, adversaire, A, B, True)

            alpha = A
            for coup in range(nbCoupsPossibles):             # On boucle sur tout les coups, qui représentent un niveau de profondeur de l'arbre
                leplateau = Plateau(plateau)                 # On copie le plateau pour ne pas modifier l'original
                leplateau.coup(coupsPossibles[coup], joueur) # On joue le coup
                                                             # Pour le dernier coup, on peut se contenter de B = alpha+1
                if profondeur == 0 and coup == nbCoupsPossibles-1:
                    resultat = self.reflexion(leplateau, profondeur+1, adversaire, alpha, alpha+1, False)
                else:
                    resultat = self.reflexion(leplateau, profondeur+1, adversaire, alpha, B, False)
                if resultat > alpha:
                    if profondeur == 0:
                        self.meilleurCoup = coupsPossibles[coup]
                    alpha = resultat
                    if alpha >= B: return alpha
                    
            return alpha   
               
               
               
               
               
               
               
               
               
               